home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Freeware / Miro 1.0 / Miro_Installer.exe / xulrunner / python / singleclick.py < prev    next >
Encoding:
Python Source  |  2007-11-12  |  12.4 KB  |  344 lines

  1. # Miro - an RSS based video player application
  2. # Copyright (C) 2005-2007 Participatory Culture Foundation
  3. #
  4. # This program is free software; you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation; either version 2 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program; if not, write to the Free Software
  16. # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  17.  
  18. """Helper functions for implement single click playback and single click
  19. torrent downloading.
  20.  
  21. Frontends should call setCommandLineArgs() passing it a list of arguments that
  22. the users gives.  This should just be suspected torrents/videos, not things
  23. like '--help', '--version', etc.
  24.  
  25. Frontends should trap when a user opens a torrent/video with democracy while
  26. democracy is already running.  They should arange for addVideo or addTorrent
  27. to be called in the existing democracy process.
  28. """
  29.  
  30. from gtcache import gettext as _
  31. import os
  32. import logging
  33. import urllib
  34.  
  35. from util import getTorrentInfoHash
  36. import app
  37. import dialogs
  38. import download_utils
  39. import item
  40. import feed
  41. import filetypes
  42. import folder
  43. import httpclient
  44. import views
  45. import platformutils
  46. import subscription
  47. import util
  48. import config
  49. import prefs
  50. from string import Template
  51.  
  52. _commandLineArgs = []
  53. commandLineVideoIds = None
  54. commandLineView = None 
  55.  
  56. def getManualFeed():
  57.     manualFeed = util.getSingletonDDBObject(views.manualFeed)
  58.     manualFeed.confirmDBThread()
  59.     return manualFeed
  60.  
  61. def addVideo(path, single = False):
  62.     path = os.path.abspath(path)
  63.     views.items.confirmDBThread()
  64.     for i in views.items:
  65.         itemFilename = i.getFilename()
  66.         if (itemFilename != '' and 
  67.                 os.path.exists(itemFilename) and
  68.                 platformutils.samefile(itemFilename, path)):
  69.             print "Not adding duplicate video: %s" % path.decode('ascii', 'ignore')
  70.             commandLineVideoIds.add(i.getID())
  71.             return
  72.     if (single):
  73.         correctFeed = util.getSingletonDDBObject(views.singleFeed)
  74.         items = [i for i in correctFeed.items]
  75.         for i in items:
  76.             i.executeExpire()
  77.     else:
  78.         correctFeed = getManualFeed()
  79.     fileItem = item.FileItem(path, feed_id=correctFeed.getID())
  80.     fileItem.markItemSeen()
  81.     commandLineVideoIds.add(fileItem.getID())
  82.  
  83. def checkURLExists(url):
  84.     manualFeed = getManualFeed()
  85.     for i in manualFeed.items:
  86.         if (i.getURL() == url):
  87.             title = _("Download already exists")
  88.             text1 = _("That URL is already an external download.")
  89.             downloadState = None
  90.             if i.downloader is not None:
  91.                 downloadState = i.downloader.getState()
  92.             if downloadState in ('paused', 'stopped'):
  93.                 i.download()
  94.                 text2 = _("Miro will begin downloading it now.")
  95.             elif downloadState == 'downloading':
  96.                 text2 = _("It is downloading now.")
  97.             else:
  98.                 text2 = _("It has already been downloaded.")
  99.             dialogs.MessageBoxDialog(title, "%s  %s" % (text1, text2)).run()
  100.             return True
  101.     existingFeed = feed.getFeedByURL(url)
  102.     if existingFeed is not None:
  103.         existingFeed.blink()
  104.         return True
  105.     return False
  106.  
  107. def addDownload(url):
  108.     if checkURLExists(url):
  109.         return
  110.     # We need to figure out if the URL is a external video link, or a link to
  111.     # a channel.
  112.     def callback(headers):
  113.         if checkURLExists(url):
  114.             return
  115.         contentType = headers.get('content-type')
  116.         if filetypes.isFeedContentType(contentType):
  117.             addFeeds([url])
  118.         else:
  119.             entry = item.getEntryForURL(url, contentType)
  120.             if filetypes.isVideoEnclosure(entry['enclosures'][0]):
  121.                 downloadVideo(entry)
  122.             else:
  123.                 downloadUnknownMimeType(url)
  124.     def errback(error):
  125.         title = _("Download Error")
  126.         text = _("""\
  127. Miro is not able to download a file at this URL:
  128.  
  129. URL: %s""") % url
  130.         dialogs.MessageBoxDialog(title, text).run()
  131.     httpclient.grabHeaders(url, callback, errback)
  132.  
  133. def downloadUnknownMimeType(url):
  134.     title = _('File Download')
  135.     text = _("""\
  136. This file at %s does not appear to be audio, video, or an RSS feed.""") % url
  137.     dialog = dialogs.ChoiceDialog(title, text, 
  138.             dialogs.BUTTON_DOWNLOAD_ANYWAY, dialogs.BUTTON_CANCEL)
  139.     def callback(dialog):
  140.         if checkURLExists(url):
  141.             return
  142.         if dialog.choice == dialogs.BUTTON_DOWNLOAD_ANYWAY:
  143.             # Fake a viedo mime type, so we will download the item.
  144.             downloadVideo(item.getEntryForURL(url, 'video/x-unknown'))
  145.     dialog.run(callback)
  146.  
  147. def downloadVideo(entry):
  148.     manualFeed = getManualFeed()
  149.     newItem = item.Item(entry, feed_id=manualFeed.getID())
  150.     newItem.download()
  151.     app.controller.selection.selectTabByTemplateBase('downloadtab')
  152.  
  153. def addTorrent(path, torrentInfohash):
  154.     manualFeed = getManualFeed()
  155.     for i in manualFeed.items:
  156.         if (i.downloader is not None and
  157.                 i.downloader.status.get('infohash') == torrentInfohash):
  158.             print ("Not downloading %s, it's already a "
  159.                     "download for %s" % (path, i))
  160.             if i.downloader.getState() in ('paused', 'stopped'):
  161.                 i.download()
  162.             return
  163.     newItem = item.Item(item.getEntryForFile(path), feed_id=manualFeed.getID())
  164.     newItem.download()
  165.  
  166. def resetCommandLineView():
  167.     global commandLineView, commandLineVideoIds
  168.     if commandLineView is not None:
  169.         commandLineView.unlink()
  170.         commandLineView = None
  171.     commandLineVideoIds = set()
  172.  
  173. def inCommandLineVideoIDs(item):
  174.     return item.getID() in commandLineVideoIds
  175. def playCommandLineView():
  176.     global commandLineView, commandLineVideoIds
  177.     if len(commandLineVideoIds) == 0:
  178.         return
  179.     commandLineView = views.items.filter(inCommandLineVideoIDs)
  180.     firstItemId = commandLineVideoIds.__iter__().next()
  181.     app.controller.playbackController.configure(commandLineView, firstItemId)
  182.     app.controller.playbackController.enterPlayback()
  183.  
  184. def addFeed(path):
  185.     feed.addFeedFromFile(path)
  186.  
  187. def addSubscriptions(path):
  188.     urls = subscription.parseFile(path)
  189.     if urls is not None:
  190.         if len(urls) > 1:
  191.             askForMultipleFeeds(urls)
  192.         else:
  193.             addFeeds(urls)
  194.  
  195. def filterExistingFeedURLs(urls):
  196.     return [u for u in urls if feed.getFeedByURL(u) is None]
  197.  
  198. def addFeeds(urls, newFolderName=None):
  199.     if len(urls) > 0:
  200.         lastFeed = None
  201.         if newFolderName is not None:
  202.             newFolder = folder.ChannelFolder(newFolderName)
  203.         for url in filterExistingFeedURLs(urls):
  204.             f = feed.Feed(url)
  205.             if newFolderName is not None:
  206.                 f.setFolder(newFolder)
  207.             lastFeed = f
  208.         if newFolderName is None:
  209.             if lastFeed:
  210.                 for url in urls:
  211.                     f = feed.getFeedByURL(url)
  212.                     if f is lastFeed:
  213.                         app.controller.selection.selectTabByObject(f)
  214.                     else:
  215.                         f.blink()
  216.             else:
  217.                 for i in xrange (len(urls) - 1):
  218.                     feed.getFeedByURL(urls[i]).blink()
  219.                 f = feed.getFeedByURL(urls[-1])
  220.                 app.controller.selection.selectTabByObject(f)
  221.         else:
  222.             app.controller.selection.selectTabByObject(newFolder)
  223.  
  224. def askForMultipleFeeds(urls):
  225.     title = _("Subscribing to multiple channels") 
  226.     description = _("Create %d channels?") % len(urls)
  227.     d = dialogs.ThreeChoiceDialog(title, description, dialogs.BUTTON_ADD,
  228.             dialogs.BUTTON_ADD_INTO_NEW_FOLDER, dialogs.BUTTON_CANCEL)
  229.     def callback(d):
  230.         if d.choice == dialogs.BUTTON_ADD:
  231.             addFeeds(urls)
  232.         elif d.choice == dialogs.BUTTON_ADD_INTO_NEW_FOLDER:
  233.             askForNewFolderName(urls)
  234.     d.run(callback)
  235.  
  236. def askForNewFolderName(urls):
  237.     newURLCount = len(filterExistingFeedURLs(urls))
  238.     existingURLCount = len(urls) - newURLCount
  239.     title = _("Adding %d channels to a new folder") % newURLCount
  240.     description = _("Enter a name for the new channel folder")
  241.     if existingURLCount > 0:
  242.         description += "\n\n"
  243.         description += _("""\
  244. NOTE: You are already subscribed to %d of these channels.  These channels \
  245. will stay where they currently are.""" % existingURLCount)
  246.  
  247.     def callback(d):
  248.         if d.choice == dialogs.BUTTON_CREATE:
  249.             addFeeds(urls, d.value)
  250.     dialogs.TextEntryDialog(title, description, dialogs.BUTTON_CREATE,
  251.             dialogs.BUTTON_CANCEL).run(callback)
  252.  
  253. def complainAboutSubscriptionURL(messageText):
  254.     title = _("Subscription error")
  255.     dialogs.MessageBoxDialog(title, messageText).run()
  256.  
  257. def addSubscriptionURL(prefix, expectedContentType, url):
  258.     realURL = url[len(prefix):]
  259.     def callback(info):
  260.         if info.get('content-type') == expectedContentType:
  261.             urls = subscription.parseContent(info['body'])
  262.             if urls is None:
  263.                 complainAboutSubscriptionURL(
  264.                     Template(_("This $shortAppName channel file has an invalid format: $url. Please notify the publisher of this file.")).substitute(url=realURL,shortAppName=config.get(prefs.SHORT_APP_NAME)))
  265.             else:
  266.                 if len(urls) > 1:
  267.                     askForMultipleFeeds(urls)
  268.                 else:
  269.                     addFeeds(urls)
  270.         else:
  271.             complainAboutSubscriptionURL(
  272.                 Template(_("This $shortAppName channel file has the wrong content type: $url. Please notify the publisher of this file.")).substitute(
  273.                 url=realURL,shortAppName=config.get(prefs.SHORT_APP_NAME)))
  274.     def errback(error):
  275.         complainAboutSubscriptionURL(
  276.                 Template(_("Could not download the $shortAppName channel file: $url.")).substitute(url=realURL,shortAppName=config.get(prefs.SHORT_APP_NAME)))
  277.     httpclient.grabURL(realURL, callback, errback)
  278.  
  279. def handleCommandLineArgs(args):
  280.     if app.controller.finishedStartup:
  281.         parseCommandLineArgs(args)
  282.     else:
  283.         setCommandLineArgs(args)
  284.  
  285. def setCommandLineArgs(args):
  286.     global _commandLineArgs
  287.     _commandLineArgs.extend (args)
  288.  
  289. def parseCommandLineArgs(args=None):
  290.  
  291.     if args is None:
  292.         global _commandLineArgs
  293.         args = _commandLineArgs
  294.         _commandLineArgs = []
  295.  
  296.     resetCommandLineView()
  297.  
  298.     addedVideos = False
  299.     addedDownloads = False
  300.  
  301.     for arg in args:
  302.         if arg.startswith('file://'):
  303.             arg = download_utils.getFileURLPath(arg)
  304.         if arg.startswith('miro:'):
  305.             addSubscriptionURL('miro:', 'application/x-miro', arg)
  306.         elif arg.startswith('democracy:'):
  307.             addSubscriptionURL('democracy:', 'application/x-democracy', arg)
  308.         elif arg.startswith('http:') or arg.startswith('https:'):
  309.             addDownload(platformutils.filenameToUnicode(arg))
  310.         elif os.path.exists(arg):
  311.             ext = os.path.splitext(arg)[1].lower()
  312.             if ext in ('.torrent', '.tor'):
  313.                 try:
  314.                     torrentInfohash = getTorrentInfoHash(arg)
  315.                 except ValueError:
  316.                     title = _("Invalid Torrent")
  317.                     msg = _("The torrent file %s appears to be corrupt and "
  318.                             "cannot be opened. [OK]") % os.path.basename(arg)
  319.                     dialogs.MessageBoxDialog(title, msg).run()
  320.                     continue
  321.                 addTorrent(arg, torrentInfohash)
  322.                 addedDownloads = True
  323.             elif ext in ('.rss', '.rdf', '.atom', '.ato'):
  324.                 addFeed(arg)
  325.             elif ext in ('.miro', '.democracy', '.dem', '.opml'):
  326.                 addSubscriptions(arg)
  327.             else:
  328.                 addVideo(arg, len(args) == 1)
  329.                 addedVideos = True
  330.         else:
  331.             print "WARNING: %s doesn't exist" % arg
  332.  
  333.     if addedVideos:
  334.         app.controller.selection.selectTabByTemplateBase('librarytab', False)
  335.         playCommandLineView()
  336.     elif addedDownloads:
  337.         app.controller.selection.selectTabByTemplateBase('downloadtab')
  338.  
  339. def openFile(path):
  340.     parseCommandLineArgs([path])
  341.  
  342. def downloadURL(url):
  343.     parseCommandLineArgs([url])
  344.